﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Data;
using System.Threading;
using VSDBM.Code.dao;
using VSDBM.Code.dto;
using System.Diagnostics;

/// <summary>
/// Summary description for SubtypingProcess
/// </summary>
public class SubtypingProcess : Process
{
    int organismId;
    int userID;
    List<Thread> runningThreads;
    int threadLimit;
    int SequenceAmount;
    
	public SubtypingProcess(int OrganismId, int userID, String JobId, int threadLimit)
	{
       //initialize process configuration
        this.type = "SubtypingProcess";
        this.name = JobId;
        this.pctComplete = 0;
        this.status = 1;
        runningThreads = new List<Thread>();
        

        //inicialize process parameters;
        this.organismId = OrganismId;
        this.userID = userID;
        this.threadLimit = threadLimit;

        //store process on the list.
        SetProcessList(this);
	
	}
    
    public override void runMethod()
    {
        Stopwatch timer = new Stopwatch();
        timer.Start();
        
       //prepara o relatório final da thread
        StringBuilder reportBuilder = new StringBuilder();
        //armazena título da thread no relatório
        reportBuilder.Append("Organism Subtype Report");
        //armazena "pula linha"
        reportBuilder.Append(" \r\n");
        //prepara lista de sequências a serem subtipadas
        List<Sequence> sequenceList = new List<Sequence>();
        //Prepara a lista de referências dos subtipos
        List<String[]> subReferenceList = new List<string[]>();
        //Preenche lista de sequências a serem subtipadas
        sequenceList = SequenceDAO.DStoList(SequenceDAO.select("WHERE idorganism =" + organismId, ""));
        //Obtém referência de subtipo, juntamente com o id do SUBTIPO.
        DataSet dsubtype = SubtypeDAO.select("WHERE idorganisms =" + organismId, "");
        bool majorerror = false;
        if (dsubtype.Tables[0].Rows.Count == 0)
        {

            reportBuilder.Append("MAJOR ERROR");
            reportBuilder.Append(" \r\n");
            reportBuilder.Append("ERROR MESSAGE: There is no subtype for the organism on DataBase.");
            reportBuilder.Append(" \r\n");
            reportBuilder.Append(" \r\n");
            reportBuilder.Append("----  THREAD ABORTED ----");
            majorerror = true;
        }
        else
        {

            foreach (DataRow row in dsubtype.Tables[0].Rows)
            {
                DataSet drefsubtype = Subtype_ReferenceDAO.select("WHERE idsubtype=" + Convert.ToInt32(row["id"]), "");

                foreach (DataRow refrow in drefsubtype.Tables[0].Rows)
                {
                    String[] auxiliar1 = new string[2];
                    auxiliar1[0] = row["id"].ToString();
                    List<Sequence> listaauxiliar = SequenceDAO.DStoList(SequenceDAO.select("WHERE id=" + refrow["idsequence"], ""));
                    auxiliar1[1] = listaauxiliar[0].sequence;
                    subReferenceList.Add(auxiliar1);
                }


            }

        }
        //Fim das obtenções

        //contagem de sucessos.
        int sucesscount = 0;
        //contagem de empates.
        int empatecount = 0;
        //prepara relatório de empares.
        StringBuilder empates = new StringBuilder();
        empates.Append("TIED SCORE REPORT");
        empates.Append(" \r\n");
        SequenceAmount = sequenceList.Count;
        if (!majorerror)
        {
            foreach (Sequence querry in sequenceList)
            {

            //inicia o processo de comparação de sequências para a subtipagem. Para cada sequência a ser subtipada, seguiremos os passos a seguir:
                //hold on til u can add another thread
              //  Thread control = new Thread(new ThreadStart(threadControl));
              //  control.Start();
               //     control.Join();
                while (runningThreads.Count >= threadLimit)
                {
                    Thread.Sleep(5000);
                }
                      //Cria lista de parametros
                            Object[] parameters = new Object[3];
                            parameters[0] = querry;
                            parameters[1] = subReferenceList;
                            //inicia novo inicializador
                            ParameterizedThreadStart starter = new ParameterizedThreadStart(alignmentThread);
                            //instancia nova thread
                            Thread t = new Thread(starter);
                            t.Name = querry.idacession;
                            parameters[2] = t;
                            //insere a thread na lista
                            runningThreads.Add(t);
                            //incia nova thread passando os parâmetros
                            t.Start(parameters); 
                        
                    
            
            
            }

            //w8 untill all sequences have finished
            Thread waintTilFinish = new Thread(new ThreadStart(waintFinish));
            waintTilFinish.Start();
            waintTilFinish.Join();

        }
        if (!majorerror)
        {
            timer.Stop();
            String elapsed = timer.Elapsed.ToString(@"hh\:mm\:ss\.FF");
           // String elapseds = DateTime.Now.Subtract(startTime).ToString(@"hh\:mm\:ss\.FF");
            reportBuilder.Append("--- Job Finished! ---");
            reportBuilder.Append(" \r\n");
            reportBuilder.Append("Time elapsed: " + elapsed);
            reportBuilder.Append(" \r\n");
            reportBuilder.Append(SequenceAmount.ToString() + " sequences subtyped.");
            reportBuilder.Append(" \r\n");
            reportBuilder.Append(empatecount.ToString() + " tied scores.");
            reportBuilder.Append(" \r\n");
            if (empatecount > 0)
            {
                reportBuilder.Append(" \r\n");
                reportBuilder.Append(empates.ToString());
            }
        }
        //Envia mensagem com o report.
        Message msg = new Message();
        msg.senduserid = userID;
        msg.touserid = userID;
        msg.title = "Job: " + name;
        msg.message = reportBuilder.ToString();
        MessageDAO.sendMessage(msg);
        this.status = 3;

    }

    public void alignmentThread(Object parameters)
    {
              Object[] parameterlist = parameters as Object[];
              Sequence querry = parameterlist[0] as Sequence;
              List<String[]> subReferenceList = parameterlist[1] as List<String[]>;
              Thread t = parameterlist[2] as Thread;
                //inicializa melhor score
                float bestscore = 0;
                //inicializa a variável que indica o id do belhor score
                int subtypeid = 0;
                //compara a sequência com cada referência disponível.
                foreach (String[] reference in subReferenceList)
                {
                    String referencesequence = reference[1];
                    String querrysequence = querry.sequence;
                    float actualScore = 0;
                    //realiza alinhamentos

                    Sequence database = new Sequence();
                    database.sequence = referencesequence.ToUpper().Replace("\r\n", "").Replace(" ", "");
                    querry.sequence = querry.sequence.ToUpper().Replace("\r\n", "").Replace(" ", "");
                    //inicia thread de alinhamento
                    SmithWatermanMMF swmmf = new SmithWatermanMMF(database, querry, new SubstitutionMatrix(), false);
                    actualScore = swmmf.highScore;

                    if (bestscore < actualScore)
                    {
                        bestscore = actualScore;
                        subtypeid = Convert.ToInt32(reference[0]);
                        //score melhor que o anterior
                    }
                }

                //armazena a subtipagem desta sequencia
                Sequence_SubtypeDAO.add(new Sequence_Subtype(0, querry.id, subtypeid, bestscore));
                //informa que a thread está completa e sobe a porcentagem
                increasepctComplete();
                //remove a thread das running threads
                runningThreads.Remove(t);
            
    }

    public void threadControl()
    {
        while (runningThreads.Count >= threadLimit)
        {
            Thread.Sleep(10000);
        }
    }

    public void waintFinish()
    {
        while (pctComplete < 100)
        {
          Thread.Sleep(60000);
               
        }
    }
    public void increasepctComplete()
    {

        pctComplete = pctComplete + (double)(100 / (double)SequenceAmount);


    }

    public override void pauseMethod()
    {
        foreach (Thread td in runningThreads)
        {
            td.Suspend();
        }
    }
    public override void resumeMethod()
    {
        foreach (Thread td in runningThreads)
        {
            td.Resume();
        }
    }






}